#!/usr/bin/env python
#
# DataFetcher.py
# Copyright (C) 2011 Piotr Foltyn
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import StocksDB
import StoppableThread
import mechanize
import re
import time
from xml.etree.ElementTree import ElementTree


_DATA_URL = 'https://notowania.mbank.com.pl/ntwweb/epromak/notowbuf'


class DataFetcher(StoppableThread.StoppableThread):
    def __init__(self, sessionId):
        super(DataFetcher, self).__init__()
        self._sessionId = str(sessionId)
        self._stocksDb = StocksDB.GetDB()
        self._cookie = None
        self._pacPap = '0'
        self._pacOfe = '0'
        self._pacTra = '0'
        self._isins = self._getAllIsins()

    def run(self):
        while not self._stop.isSet():
            try:
                body = self._getPostBody()
                response = self._openAndReturnResponse(body)
                tree = ElementTree()
                tree.parse(response)
                if None == self._cookie:
                    self._updateCookie(response)
                self._updatePacParameters(tree)
                self._updateDatabase(tree)
            except:
                pass
            time.sleep(1)

    def _getAllIsins(self):
        command = StocksDB.GetAllIsinsCommand()
        self._stocksDb.sendCommand(command)
        command.wait()
        isins = {}
        for isin in command.isins:
            isins[isin] = {
                'lastPrice': float('-inf'),
                'lowestPrice': float('inf'),
                'highestPrice': float('-inf')
            }
        return isins

    def _updateCookie(self, response):
        for header in response.info().getallmatchingheaders('Set-Cookie'):
            if header.find('ARPT') != -1:
                arptId = re.sub('[\s\S]*: |;[\s\S]*', '', header)
            elif header.find('JSESSIONID') != -1:
                jSessionId = re.sub('[\s\S]*: |;[\s\S]*', '', header)
        self._cookie = '%s; %s;' % (jSessionId, arptId)

    def _updatePacParameters(self, tree):
        dataset = tree.find('dataset')
        self._pacPap = dataset.attrib['pac']
        self._pacOfe = dataset.attrib['paco']
        self._pacTra = dataset.attrib['pact']

    def _updateDatabase(self, tree):
        marketDate = tree.find('dataset').attrib['czas'].split()[0]
        for stock in list(tree.iter('pRdfPapiery')):
            data = stock.attrib['data'].split('@')
            lastPrice = filter(None, data[0].split('|'))
            prices = filter(None, data[9].split('|'))
            volumes = filter(None, data[10].split('|'))
            times = filter(None, data[11].split('|'))
            if len(lastPrice) > 0:
                self._isins[stock.attrib['isin']]['lastPrice'] = float(lastPrice[-1])
            for index in xrange(len(prices)):
                command = StocksDB.AddTransactionCommand()
                command.isin = stock.attrib['isin']
                command.datetime = "%s %s" % (marketDate, times[index])
                self._isins[stock.attrib['isin']]['highestPrice'] = max(self._isins[stock.attrib['isin']]['highestPrice'], float(prices[index]))
                self._isins[stock.attrib['isin']]['lowestPrice'] = min(self._isins[stock.attrib['isin']]['lowestPrice'], float(prices[index]))
                command.high = self._isins[stock.attrib['isin']]['highestPrice']
                command.low = self._isins[stock.attrib['isin']]['lowestPrice']
                command.open = self._isins[stock.attrib['isin']]['lastPrice']
                command.close = prices[index]
                command.volume = volumes[index]
                self._stocksDb.sendCommand(command)
                self._isins[stock.attrib['isin']]['lastPrice'] = prices[index]

    def _openAndReturnResponse(self, body):
        request = mechanize.Request(_DATA_URL)
        if None != self._cookie:
            request.add_header('Cookie', self._cookie)
        request.add_header('Accept-Encoding', 'gzip')
        request.add_header('Content-Type', 'application/x-www-form-urlencoded')
        request.add_header('Content-Encoding', 'UTF-8')
        request.add_header('sessnum', self._sessionId)
        request.add_header('login', '')
        return mechanize.urlopen(request, body)

    def _getStocksXml(self):
        stocksXml = ''
        if None == self._cookie:
            for isin in self._isins.keys():
                stocksXml += '<pRdfPapiery isin="%s"/>' % isin
        return stocksXml

    def _getPostBody(self):
        postBody = \
            '<EPROMAK><ERR code="0"/><ACT name="selectRdf"/>' + \
            '<CTX login="" sessnum="' + self._sessionId + '"/>' + \
            '<dataset name="pRdfPapiery" pac="' + self._pacPap + '" paco="' + self._pacOfe + '" pact="' + self._pacTra + '" ileOfert="1">' + \
            self._getStocksXml() + \
            '</dataset></EPROMAK>'
        return postBody
